use std::collections::HashSet;

use chrono::Local;
use shq_common::prelude::{empl::EmployeeCache, TaskStatus};

use crate::{
    config::CounterSignType,
    executor::{
        cache::{ExecutorInfo, FlowCache, SaveType},
        get_executor,
    },
};

use super::datebase::{insert_step_user_state, start_step, update_step_user_state};

pub(crate) struct StepInstance {
    run_id: u64,
    step_id: u8,
    executor: Vec<ExecutorInfo>,
    user_infos: Vec<ExecutorInfo>,
    is_vote: bool,
}

impl StepInstance {
    pub(crate) async fn from(
        run_id: u64,
        step_id: u8,
        is_vote: bool,
        tx: Option<&mut sqlx::Transaction<'_, sqlx::MySql>>,
    ) -> anyhow::Result<Self> {
        let executor = get_executor(run_id, step_id, tx).await?;
        let user_infos: Vec<ExecutorInfo> = Vec::new();
        Ok(Self {
            run_id,
            step_id,
            executor,
            user_infos,
            is_vote,
        })
    }
    pub(crate) fn get_step_id(&self) -> u8 {
        self.step_id
    }
    pub(crate) fn get_executor(&self) -> &Vec<ExecutorInfo> {
        self.executor.as_ref()
    }
    pub(crate) async fn start(
        &mut self,
        tx: &mut sqlx::Transaction<'_, sqlx::MySql>,
    ) -> anyhow::Result<()> {
        let is_start = self
            .executor
            .iter()
            .any(|e| e.task_state != TaskStatus::None || e.start_time.is_some());
        let users: Vec<ExecutorInfo> = self
            .executor
            .iter()
            .map(|e| {
                if !is_start {
                    ExecutorInfo {
                        user_id: e.user_id,
                        task_state: TaskStatus::Doing,
                        start_time: Some(Local::now()),
                        submit_time: None,
                        feed_back: None,
                        save_type: SaveType::None,
                    }
                } else {
                    let st = if e.task_state == TaskStatus::Done
                        || e.task_state == TaskStatus::Suspend
                        || e.task_state == TaskStatus::Back
                        || e.task_state == TaskStatus::None
                    {
                        TaskStatus::Doing
                    } else {
                        e.task_state
                    };
                    ExecutorInfo {
                        user_id: e.user_id,
                        task_state: st,
                        start_time: Some(Local::now()),
                        submit_time: None,
                        feed_back: None,
                        save_type: SaveType::None,
                    }
                }
            })
            .collect();
        let emids = users
            .iter()
            .filter(|u| u.task_state == TaskStatus::Doing)
            .map(|u| u.user_id)
            .collect::<Vec<_>>();
        start_step(self.run_id, self.step_id, emids, is_start, tx).await?;
        self.user_infos = users;
        Ok(())
    }
    /// 在step上执行退回操作
    pub(crate) async fn seed_back(
        &mut self,
        user_id: u32,
        sign_type: CounterSignType,
        feed_back: Option<String>,
        tx: &mut sqlx::Transaction<'_, sqlx::MySql>,
    ) -> anyhow::Result<bool> {
        self.verfy_user_can_do(user_id)?;
        let is_f = self.is_finish(sign_type, user_id).await?;
        let users =
            self.update_user_state_to_vec(user_id, TaskStatus::Back, feed_back.as_ref(), is_f);
        update_step_user_state(self.run_id, self.step_id, &users, tx).await?;
        self.user_infos = users;
        Ok(is_f)
    }
    /// 在step上执行提交操作
    pub(crate) async fn seed_next(
        &mut self,
        user_id: u32,
        sign_type: CounterSignType,
        feed_back: Option<String>,
        is_manager: bool,
        tx: &mut sqlx::Transaction<'_, sqlx::MySql>,
    ) -> anyhow::Result<bool> {
        let mut feed_back = feed_back;
        if !is_manager {
            self.verfy_user_can_do(user_id)?;
        }
        if user_id == 0 && feed_back.is_none() {
            feed_back = Some("系统自动提交".to_string());
        }
        let is_f: bool;
        let users = if !is_manager {
            is_f = self.is_finish(sign_type, user_id).await?;
            let users =
                self.update_user_state_to_vec(user_id, TaskStatus::Done, feed_back.as_ref(), is_f);
            update_step_user_state(self.run_id, self.step_id, &users, tx).await?;
            users
        } else {
            is_f = true;
            let mut users =
                self.update_user_state_to_vec(user_id, TaskStatus::Done, feed_back.as_ref(), is_f);
            update_step_user_state(self.run_id, self.step_id, &users, tx).await?;
            let sui = ExecutorInfo::new(
                user_id,
                TaskStatus::Done,
                Some(Local::now()),
                Some(Local::now()),
                feed_back,
                SaveType::Insert,
            );
            insert_step_user_state(self.run_id, self.step_id, &sui, tx).await?;
            users.push(sui);
            users
        };
        self.user_infos = users;
        Ok(is_f)
    }
    /// 在step上执行中止操作
    pub(crate) async fn suspend(
        &mut self,
        user_id: u32,
        feed_back: Option<String>,
        tx: &mut sqlx::Transaction<'_, sqlx::MySql>,
    ) -> anyhow::Result<()> {
        self.verfy_user_can_do(user_id)?;
        let users =
            self.update_user_state_to_vec(user_id, TaskStatus::Suspend, feed_back.as_ref(), true);
        update_step_user_state(self.run_id, self.step_id, &users, tx).await?;
        self.user_infos = users;
        Ok(())
    }
    /// 在step上执行取回操作
    pub(crate) async fn take_back(
        &mut self,
        user_id: u32,
        feed_back: Option<String>,
        tx: &mut sqlx::Transaction<'_, sqlx::MySql>,
    ) -> anyhow::Result<bool> {
        let to_step_id = self.step_id - 1;
        if to_step_id == 0 {
            anyhow::bail!("已经是第一步，不能取回");
        }
        let es = get_executor(self.run_id, to_step_id, Some(tx)).await?;
        let is_can = es
            .iter()
            .any(|e| e.user_id == user_id && e.task_state == TaskStatus::Done);
        if !is_can {
            anyhow::bail!("用户user_id:{}不能取回", user_id);
        }
        let users =
            self.update_user_state_to_vec(user_id, TaskStatus::None, feed_back.as_ref(), true);
        update_step_user_state(self.run_id, self.step_id, &users, tx).await?;
        self.user_infos = users;
        Ok(true)
    }
    /// 标记步骤未开始
    // pub(crate) async fn not_start(
    //     &mut self,
    //     user_id: u32,
    //     tx: &mut sqlx::Transaction<'_, sqlx::MySql>,
    // ) -> anyhow::Result<bool> {
    //     let users = self.update_user_state_to_vec(user_id, TaskStatus::None, None, self.is_vote);
    //     update_step_user_state(self.run_id, self.step_id, &users, tx).await?;
    //     self.user_infos = users;
    //     Ok(true)
    // }
    pub(crate) async fn is_vote_ok(&self, sign_type: CounterSignType) -> anyhow::Result<bool> {
        if !self.is_vote {
            return Ok(true);
        }
        let users = get_executor(self.run_id, self.step_id, None).await?;
        let is_ok = match sign_type {
            CounterSignType::All => {
                let count = users.len();
                let done_count = users
                    .iter()
                    .filter(|u| u.task_state == TaskStatus::Done)
                    .count();
                done_count * 2 + 1 > count
            }
            CounterSignType::AllDept => {
                let mut depts: HashSet<u32> = HashSet::new();
                let mut finish_depts: HashSet<u32> = HashSet::new();
                let mut ec = EmployeeCache::new()?;
                for e in self.executor.iter() {
                    let dpid = ec.get_dept_id_by_user_id(e.user_id).await?;
                    depts.insert(dpid);
                    if e.task_state == TaskStatus::Done {
                        finish_depts.insert(dpid);
                    }
                }
                finish_depts.len() * 2 + 1 > depts.len()
            }
            CounterSignType::Any => false,
        };
        Ok(is_ok)
    }
    pub(crate) fn cache_user_infos(&mut self) -> anyhow::Result<()> {
        let mut fc = FlowCache::from(self.run_id)?;
        fc.set_step_users(self.step_id, &self.user_infos)?;
        Ok(())
    }
    fn update_user_state_to_vec(
        &self,
        user_id: u32,
        ts: TaskStatus,
        feed_back: Option<&String>,
        is_finish: bool,
    ) -> Vec<ExecutorInfo> {
        self.executor
            .iter()
            .map(|e| {
                let mut sui = ExecutorInfo::new(
                    e.user_id,
                    e.task_state,
                    e.start_time,
                    e.submit_time,
                    e.feed_back.as_ref().map(|fb| fb.clone()),
                    SaveType::None,
                );
                if e.user_id == user_id {
                    sui.task_state = ts;
                    sui.feed_back = feed_back.map(|fb| fb.clone());
                    match ts {
                        TaskStatus::None => {
                            sui.submit_time = None;
                            sui.save_type = SaveType::Update;
                        }
                        TaskStatus::Doing => {
                            sui.start_time = Some(Local::now());
                            sui.submit_time = None;
                            sui.save_type = SaveType::Update;
                        }
                        TaskStatus::Done => {
                            sui.submit_time = Some(Local::now());
                            sui.save_type = SaveType::Update;
                        }
                        TaskStatus::Suspend | TaskStatus::Back => {
                            sui.submit_time = Some(Local::now());
                            sui.save_type = SaveType::Update;
                        }
                    }
                } else if is_finish {
                    if sui.task_state == TaskStatus::Doing {
                        if ts == TaskStatus::Suspend || ts == TaskStatus::Doing {
                            sui.task_state = TaskStatus::None;
                            sui.save_type = SaveType::Update;
                        } else if ts == TaskStatus::Back {
                            sui.task_state = TaskStatus::None;
                            sui.save_type = SaveType::Update;
                        } else if ts == TaskStatus::Done {
                            sui.task_state = TaskStatus::None;
                            sui.save_type = SaveType::Update;
                        } else if ts == TaskStatus::None || ts == TaskStatus::Back {
                            sui.task_state = TaskStatus::None;
                            sui.save_type = SaveType::Update;
                        }
                    }
                }
                sui
            })
            .collect()
    }
    fn verfy_user_can_do(&self, user_id: u32) -> anyhow::Result<()> {
        let is_curr_user = self
            .executor
            .iter()
            .any(|e| e.user_id == user_id && e.task_state == TaskStatus::Doing);
        if !is_curr_user {
            anyhow::bail!("用户user_id:{}不能执行该操作", user_id)
        }
        Ok(())
    }
    async fn is_finish(&self, sign_type: CounterSignType, user_id: u32) -> anyhow::Result<bool> {
        match sign_type {
            CounterSignType::Any => Ok(true),
            CounterSignType::All => {
                let is_ok = self.executor.iter().all(|e| {
                    e.task_state == TaskStatus::Done
                        || e.task_state == TaskStatus::Back
                        || e.user_id == user_id
                });
                Ok(is_ok)
            }
            CounterSignType::AllDept => {
                let mut depts: HashSet<u32> = HashSet::new();
                let mut finish_depts: HashSet<u32> = HashSet::new();
                let mut ec = EmployeeCache::new()?;
                for e in self.executor.iter() {
                    let dpid = ec.get_dept_id_by_user_id(e.user_id).await?;
                    depts.insert(dpid);
                    if e.task_state == TaskStatus::Done
                        || e.task_state == TaskStatus::Back
                        || e.user_id == user_id
                    {
                        finish_depts.insert(dpid);
                    }
                }
                Ok(depts.len() == finish_depts.len())
            }
        }
    }
}
